#include "CConsole.h"

CConsole::CConsole()
{
    m_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    m_hInput = GetStdHandle(STD_INPUT_HANDLE);
    m_hError = GetStdHandle(STD_ERROR_HANDLE);
    m_hWnd = GetConsoleWindow();
    m_strPrefixTips = ">: ";
}

CConsole::~CConsole()
{
}

CConsole* CConsole::GetInstance()
{
    static CConsole ConsoleInteract;
    return &ConsoleInteract;
}

void CConsole::SetPrefixTips(IN std::string str)
{
    m_strPrefixTips = str;
}

void CConsole::SetWindowTitle(IN LPCTSTR lpString)
{
    SetConsoleTitle(lpString);
}

BOOL CConsole::GetConsoleScreenBufferInfo(OUT PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo)
{
    return ::GetConsoleScreenBufferInfo(m_hOutput, lpConsoleScreenBufferInfo);
}

BOOL CConsole::SetConsoleScreenBufferSize(IN COORD dwSize)
{
    return ::SetConsoleScreenBufferSize(m_hOutput, dwSize);
}

BOOL CConsole::SetWindowTopMost(IN BOOL bTopMost)
{
    if (bTopMost)
        return SetWindowPos(m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
    else
        return SetWindowPos(m_hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}

void CConsole::ClearScreen()
{
    system("cls");
}

BOOL CConsole::SetCurrentConsoleFont(IN LPCWSTR lpFont, IN unsigned int height, IN unsigned int width)
{
    CONSOLE_FONT_INFOEX FontInfo = { 0 };
    FontInfo.cbSize = sizeof(CONSOLE_FONT_INFOEX);

    FontInfo.dwFontSize.X = width;
    FontInfo.dwFontSize.Y = height;
    FontInfo.FontFamily = FF_DONTCARE;
    FontInfo.FontWeight = FW_NORMAL;
    RtlCopyMemory(FontInfo.FaceName, lpFont, wcslen(lpFont) * sizeof(WCHAR));

    return SetCurrentConsoleFontEx(m_hOutput, FALSE, &FontInfo);
}

BOOL CConsole::SetConsoleCursorSize(IN DWORD dwSize)
{
    CONSOLE_CURSOR_INFO cci = { 0 };
    cci.dwSize = dwSize;
    cci.bVisible = TRUE;
    return SetConsoleCursorInfo(m_hOutput, &cci);
}

void CConsole::SplitString(IN std::string strInput, OUT std::vector<std::string>& vtStr)
{
    std::istringstream is(strInput);
    std::string str;

    while (std::getline(is, str, ' '))
    {
        vtStr.push_back(str);
    }
}

void CConsole::InputStringParse(IN std::string& strInput, OUT std::string& strCommand, OUT std::vector<std::string>& vtParameter)
{
    std::vector<std::string> vtStr;
    std::map<std::string, PCONSOLE_USER_DEFINE>::iterator it;
    std::string str;

    SplitString(strInput, vtStr);
    if (!vtStr.size()) return;

    strCommand = vtStr[0];
    it = m_CommandMap.find(vtStr[0]);
    if (it == m_CommandMap.end()) return;

    for (unsigned int i = 1; i < vtStr.size(); i++)
    {
        vtParameter.push_back(vtStr[i]);
    }
}

BOOL CConsole::CallHandleProc(IN std::string& strInput, IN std::string& strCommand, IN std::vector<std::string>& argv, OUT BOOL& bExit)
{
    if (strCommand != "")
    {
        auto it = m_CommandMap.find(strCommand);
        if (it != m_CommandMap.end())
        {
            it->second->lpCommandHandleProc(strInput, strCommand, argv, it->second->lpUserPtr, bExit);
            return TRUE;
        }
    }
    return FALSE;
}

void CConsole::OutputStringPrint(std::string str)
{
    std::cout << str;
}

void CConsole::Interact()
{
    std::string strInput;
    std::string strCommand;
    std::vector<std::string> vtParameter;
    BOOL bExit;

    while (TRUE)
    {
        OutputStringPrint(m_strPrefixTips);

        std::getline(std::cin, strInput);

        if (strInput != "")
        {
            strCommand = "";
            vtParameter.clear();
            InputStringParse(strInput, strCommand, vtParameter);
        }

        bExit = FALSE;
        if (!CallHandleProc(strInput, strCommand, vtParameter, bExit))
        {
            std::string strErrorTips = "No Command \'" + strCommand + "\'" + "\r\n";
            OutputStringPrint(strErrorTips);
        }

        if (bExit) break;
    }
}

void CConsole::RegisterCommand(IN std::string strCommand, IN CommandHandleProc lpHandlerProc, IN void* lpUserPtr)
{
    if (strCommand == "") return;

    PCONSOLE_USER_DEFINE lpUserDefine = new CONSOLE_USER_DEFINE;
    if (!lpUserDefine) return;
    RtlZeroMemory(lpUserDefine, sizeof(CONSOLE_USER_DEFINE));

    lpUserDefine->lpCommandHandleProc = lpHandlerProc;
    lpUserDefine->lpUserPtr = lpUserPtr;
    m_CommandMap[strCommand] = lpUserDefine;
}

